package com.yvdelx.cyclowomen.business;

import com.yvdelx.cyclowomen.object.Cycle;
import com.yvdelx.cyclowomen.object.Day;
import com.yvdelx.cyclowomen.object.EnumNature;
import com.yvdelx.cyclowomen.object.Profile;
import com.yvdelx.cyclowomen.utility.DateUtility;
import com.yvdelx.cyclowomen.utility.LogUtility;
import com.yvdelx.cyclowomen.utility.MathUtility;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * Created by ydelvaux on 06/03/14.
 * Business of cycle object
 */
public class CycleBusiness {

    // This object is used only for logging
    private static Class cycleBusinessClass = CycleBusiness.class;

    /**
     * Get the day of cycle that correspond at the date in param
     * @param date Date
     * @param cycle Cycle
     * @return null if not found else Day object
     */
    public static Day getDay(Date date, Cycle cycle){
        LogUtility.debug(cycleBusinessClass, "Date parameter : " + date.toString());
        for(Day day : cycle.getLstDays()){
            if(day.getDate().compareTo(date) == 0){
                return day;
            }
        }
        return null;
    }


    /**
     * Return the ID of the next cycle
     * @return String ID
     */
    public static String getNextID(Profile profile){
        // Algorithm very easy to generate an unique id
        List<Cycle> cycles = profile.getCycleList();
        Collections.sort(cycles);
        Integer size = cycles.size();
        Integer id;
        if(size > 0)
            id = Integer.valueOf(cycles.get(size - 1).getIdCycle());
        else
            id = 1;
        id++;
        return String.valueOf(id);
    }

    /**
     * Add a day to this cycle
     * @param day Day
     */
    public static void addDay(Day day, Cycle cycle){
        // Get the day at the date to verify if it exists or not
        Day existingDay = getDay(day.getDate(), cycle);

        // If the day exist, it is removed
        if(existingDay != null){
            deleteDay(existingDay,cycle);
        }

        // If the day is the first day of a cycle, the cycle begin date is moved
        if(cycle.getDateDebut() == null || day.getDate().before(cycle.getDateDebut())){
            cycle.setDateDebut(day.getDate());
        }

        // Add the new day and compute the cycle
        cycle.getLstDays().add(day);
        cycle.setComputed(false);
        computeNatureDays(cycle);
    }

    /**
     * Get the date of the last day of the cycle
     * @return Date date
     */
    public static Date getCurrentDate(Cycle cycle) {
        // Get the last day of the cycle
        Day day = getLastDay(cycle);
        if(day != null)
            return day.getDate();
        else
            return null;
    }

    /**
     * Delete an item in the day list
     * @param day Day (object to delete)
     */
    public static void deleteDay(Day day, Cycle cycle) {
        cycle.getLstDays().remove(day);
    }

    /**
     * Get next Day of the day in param
     * isSearchNextDay = true --> search the next day of day in param
     * @param day Day
     * @param cycle Cycle
     * @param isSearchNextDay Boolean
     * @return Day
     */
    public static Day getNextDay(Day day, Cycle cycle, Boolean isSearchNextDay) {
        // Get the date of the day in param
        Date actualDate = day.getDate();
        Day result;
        Integer indexSafeLoop = 0;

        // Safe loop : after 2 months, day is not searched
        while(indexSafeLoop <= 60){
            if(isSearchNextDay)
                actualDate = DateUtility.nextDate(actualDate);
            else
                actualDate = DateUtility.previousDate(actualDate);
            result = getDay(actualDate, cycle);
            if(result != null){
                return result;
            }
            indexSafeLoop++;
        }
        return null;
    }

    /**
     * Compute Nature days of cycle
     * @param cycle Cycle
     */
    public static void computeNatureDays(Cycle cycle) {
        if(cycle.isComputed())
            return;
        // Initialize variable
        Float averageLowTemperature = 300.0f; // To prevent false positive
        Float temperatureDay;
        Float thresholdTemp;
        List<Day> result = new ArrayList<Day>();
        List<Day> natureUndetermined = new ArrayList<Day>();
        List<Float> previousTemperatureLow = new ArrayList<Float>();
        Integer counterHighPoint = 0;
        Integer counterDays = 0;

        for(Day day : cycle.getLstDays()){
            // The day is not take if the condition of analyze is not nominal
            if(day.getIsNominalCondition()) {
                counterDays++;
                temperatureDay = day.getTemperature();
                thresholdTemp = averageLowTemperature + 0.3f;

                if((temperatureDay >= thresholdTemp || counterHighPoint >= 4)
                        && counterDays > 6){
                    counterHighPoint++;
                    if(counterHighPoint < 4){
                        natureUndetermined.add(day);
                    } else {
                        natureUndetermined = new ArrayList<Day>();
                    }
                    day.setNature(EnumNature.HIGH_POINT);
                } else {
                    if(counterHighPoint > 0){
                        counterHighPoint = 0;
                        for(Day dayUndetermined : natureUndetermined){
                            result.remove(dayUndetermined);
                            dayUndetermined.setNature(EnumNature.LOW_POINT);
                            previousTemperatureLow.add(dayUndetermined.getTemperature());
                            result.add(dayUndetermined);
                        }
                        natureUndetermined = new ArrayList<Day>();
                    }
                    day.setNature(EnumNature.LOW_POINT);
                    previousTemperatureLow.add(day.getTemperature());
                    averageLowTemperature = MathUtility.computeAverage(previousTemperatureLow);
                }
            }
            result.add(day);
        }

        if(!natureUndetermined.isEmpty()){
            for(Day dayUndetermined : natureUndetermined){
                result.remove(dayUndetermined);
                dayUndetermined.setNature(EnumNature.LOW_POINT);
                result.add(dayUndetermined);
            }
        }

        Collections.sort(result); // Sort days by asc
        cycle.setLstDays(result); // Save the sorted list
        cycle.setAverageHighTemperature((averageLowTemperature + 0.3f));
        cycle.setAverageLowTemperature(averageLowTemperature);
        cycle.setComputed(true);
        LogUtility.debug(cycleBusinessClass,cycle.toString());
    }

    /**
     * Return the last day of a cycle
     * @param cycle Cycle
     * @return Day lastDay
     */
    public static Day getLastDay(Cycle cycle) {
        int size = cycle.getLstDays().size();
        return (cycle.getLstDays().get(size-1));
    }
}
